"use strict";

function _instanceof(left, right) { if (right != null && typeof Symbol !== "undefined" && right[Symbol.hasInstance]) { return !!right[Symbol.hasInstance](left); } else { return left instanceof right; } }

function _createForOfIteratorHelper(o, allowArrayLike) { var it; if (typeof Symbol === "undefined" || o[Symbol.iterator] == null) { if (Array.isArray(o) || (it = _unsupportedIterableToArray(o)) || allowArrayLike && o && typeof o.length === "number") { if (it) o = it; var i = 0; var F = function F() {}; return { s: F, n: function n() { if (i >= o.length) return { done: true }; return { done: false, value: o[i++] }; }, e: function e(_e) { throw _e; }, f: F }; } throw new TypeError("Invalid attempt to iterate non-iterable instance.\nIn order to be iterable, non-array objects must have a [Symbol.iterator]() method."); } var normalCompletion = true, didErr = false, err; return { s: function s() { it = o[Symbol.iterator](); }, n: function n() { var step = it.next(); normalCompletion = step.done; return step; }, e: function e(_e2) { didErr = true; err = _e2; }, f: function f() { try { if (!normalCompletion && it.return != null) it.return(); } finally { if (didErr) throw err; } } }; }

function _unsupportedIterableToArray(o, minLen) { if (!o) return; if (typeof o === "string") return _arrayLikeToArray(o, minLen); var n = Object.prototype.toString.call(o).slice(8, -1); if (n === "Object" && o.constructor) n = o.constructor.name; if (n === "Map" || n === "Set") return Array.from(o); if (n === "Arguments" || /^(?:Ui|I)nt(?:8|16|32)(?:Clamped)?Array$/.test(n)) return _arrayLikeToArray(o, minLen); }

function _arrayLikeToArray(arr, len) { if (len == null || len > arr.length) len = arr.length; for (var i = 0, arr2 = new Array(len); i < len; i++) { arr2[i] = arr[i]; } return arr2; }

(function (g) {
  var API_URL = {
    MAIN: "/api",
    TASKS: "/tasks",
    COMPLETIONS: "/completions",
    DRAFTS: "/drafts",
    CANCEL: "/cancel",
    PROJECTS: "/projects",
    NEXT: "/next",
    EXPERT_INSRUCTIONS: "/expert_instruction"
  };
  var HOST = "https://app.heartex.ai";
  /*
   * Low level code to interact with the API
   */

  var Requests = function Requests(token) {
    var handleResponse = function handleResponse(res) {
      if (res.status !== 200 || res.status !== 201) {
        return res;
      } else {
        return res.json();
      }
    };

    var wrapperRequest = function wrapperRequest(url, method, headers, body) {
      url = HOST + url;
      headers = headers || {};
      if (token) headers["Authorization"] = "Token " + token;
      var reqOpts = {
        method: method,
        headers: headers,
        body: body
      };

      if (!token) {
        reqOpts["credentials"] = "include";
      }

      return window.fetch(url, reqOpts).then(function (response) {
        return handleResponse(response);
      });
    };

    var fetcher = function fetcher(url) {
      return wrapperRequest(url, "GET", {
        Accept: "application/json"
      });
    };

    var poster = function poster(url, body) {
      return wrapperRequest(url, "POST", {
        Accept: "application/json",
        "Content-Type": "application/json"
      }, body);
    };

    var patch = function patch(url, body) {
      return wrapperRequest(url, "PATCH", {
        Accept: "application/json",
        "Content-Type": "application/json"
      }, body);
    };

    var remover = function remover(url, body) {
      return wrapperRequest(url, "DELETE", {
        "Content-Type": "application/json"
      }, body);
    };

    return {
      fetcher: fetcher,
      poster: poster,
      patch: patch,
      remover: remover
    };
  };
  /*
   * Serializers are used to convert Label Studio format to Heartex
   * API format and vice-versa
   */


  var Serializers = function () {
    var _completionToAPI = function _completionToAPI(c, append) {
      var data = c.serializeCompletion();
      var completion = {
        lead_time: (new Date() - c.loadedDate) / 1000,
        // task execution time
        result: data,
        draft_id: c.draftId
      };
      if (append && typeof append === "object") {
        Object.assign(completion, append);
      }
      var body = JSON.stringify(completion);
      return body;
    };

    var _taskToLS = function _taskToLS(task) {
      if (!task) return;

      if (task.completions) {
        var _iterator = _createForOfIteratorHelper(task.completions),
            _step;

        try {
          for (_iterator.s(); !(_step = _iterator.n()).done;) {
            var tc = _step.value;
            tc.pk = tc.id;
            tc.createdAgo = tc.created_ago;
            tc.createdBy = tc.created_username;
            tc.leadTime = tc.lead_time;
          }
        } catch (err) {
          _iterator.e(err);
        } finally {
          _iterator.f();
        }
      }

      if (task.predictions) {
        var _iterator2 = _createForOfIteratorHelper(task.predictions),
            _step2;

        try {
          for (_iterator2.s(); !(_step2 = _iterator2.n()).done;) {
            var tp = _step2.value;
            tp.pk = tp.pk;
            tp.createdAgo = tp.created_ago;
            tp.createdBy = tp.model_version;
          }
        } catch (err) {
          _iterator2.e(err);
        } finally {
          _iterator2.f();
        }
      }

      return task;
    };

    return {
      completionToAPI: _completionToAPI,
      taskToLS: _taskToLS
    };
  }();
  /*
   * SDK Code
   */


  function HeartexSDK() {
    var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
        _ref$usePrediction = _ref.usePrediction,
        usePrediction = _ref$usePrediction === void 0 ? false : _ref$usePrediction,
        _ref$mode = _ref.mode,
        mode = _ref$mode === void 0 ? "explore" : _ref$mode,
        _ref$elid = _ref.elid,
        elid = _ref$elid === void 0 ? "label-studio" : _ref$elid,
        token = _ref.token,
        user = _ref.user,
        config = _ref.config,
        task = _ref.task,
        project = _ref.project,
        _ref$interfaces = _ref.interfaces,
        interfaces = _ref$interfaces === void 0 ? ["basic", "controls", "ground-truth", "side-column", "submit", "update"] : _ref$interfaces,
        options = _ref.options,
        _ref$onDeleteCompleti = _ref.onDeleteCompletion,
        onDeleteCompletion = _ref$onDeleteCompleti === void 0 ? function (_) {
      return _;
    } : _ref$onDeleteCompleti,
        _ref$onEntityCreate = _ref.onEntityCreate,
        onEntityCreate = _ref$onEntityCreate === void 0 ? function (_) {
      return _;
    } : _ref$onEntityCreate,
        _ref$onEntityDelete = _ref.onEntityDelete,
        onEntityDelete = _ref$onEntityDelete === void 0 ? function (_) {
      return _;
    } : _ref$onEntityDelete,
        _ref$onLabelStudioLoa = _ref.onLabelStudioLoad,
        onLabelStudioLoad = _ref$onLabelStudioLoa === void 0 ? function (_) {
      return _;
    } : _ref$onLabelStudioLoa,
        _ref$onSkipTask = _ref.onSkipTask,
        onSkipTask = _ref$onSkipTask === void 0 ? function (_) {
      return _;
    } : _ref$onSkipTask,
        _ref$onSubmitCompleti = _ref.onSubmitCompletion,
        onSubmitCompletion = _ref$onSubmitCompleti === void 0 ? function (_) {
      return _;
    } : _ref$onSubmitCompleti,
        _ref$onTaskLoad = _ref.onTaskLoad,
        onTaskLoad = _ref$onTaskLoad === void 0 ? function (_) {
      return _;
    } : _ref$onTaskLoad,
        _ref$onUpdateCompleti = _ref.onUpdateCompletion,
        onUpdateCompletion = _ref$onUpdateCompleti === void 0 ? function (_) {
      return _;
    } : _ref$onUpdateCompleti;

    if (typeof user === "number") user = {
      pk: user
    };

    this.opts = {
      mode: mode,
      config: config,
      elid: elid,
      interfaces: interfaces,
      project: project,
      task: task,
      token: token,
      user: user,
      usePrediction: usePrediction,
      options: options
    };
    this.cbs = {
      onDeleteCompletion: onDeleteCompletion,
      onEntityCreate: onEntityCreate,
      onEntityDelete: onEntityDelete,
      onLabelStudioLoad: onLabelStudioLoad,
      onSkipTask: onSkipTask,
      onSubmitCompletion: onSubmitCompletion,
      onTaskLoad: onTaskLoad,
      onUpdateCompletion: onUpdateCompletion
    };
    this.loadedTasks = [];
    this.reqs = Requests(token); // if config was not provided then we need to call Heartex API to
    // fetch that, without that we can't render the labeling interface

    if (this.opts.config === undefined && this.opts.project > 0) {
      this.loadProject(this.opts.project, function () {
        return task && self.labelTask(task);
      });
    } else {
      // initialize label studio instance and load the task for
      // labeling if it was provided through the initialization
      this.studio = this._initLabelStudio();
      task && this.labelTask(task);
    }
  }
  /*
   * Public Methods
   */


  HeartexSDK.prototype.loadProject = function (projectID) {
    var cb = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : function (_) {
      return _;
    };

    /*
     * Load config from a specific project, this will also reset the
     * label studio instance
     */
    var self = this;
    this.reqs.fetcher("".concat(API_URL.MAIN).concat(API_URL.PROJECTS, "/").concat(projectID)).then(function (res) {
      res.json().then(function (res) {
        self.opts.project = projectID;
        self.opts.config = res.label_config;
        self.studio = self._initLabelStudio();
        cb();
      });
    });
  };

  HeartexSDK.prototype.labelTask = function (taskID) {
    var opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {
      addInitialWhenEmpty: false,
      usePrediction: this.opts.usePrediction
    };
    var url = "".concat(API_URL.MAIN).concat(API_URL.TASKS, "/").concat(taskID, "/");

    this._addTask(taskID);

    return this._loadTask(url, opts);
  };

  HeartexSDK.prototype.labelNextTask = function () {
    var opts = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {
      usePrediction: this.opts.usePrediction
    };
    var self = this;
    var url = "".concat(API_URL.MAIN).concat(API_URL.PROJECTS, "/").concat(self.opts.project).concat(API_URL.NEXT);
    return this._loadTask(url, {
      addInitialWhenEmpty: true,
      usePrediction: opts.usePrediction
    });
  };

  HeartexSDK.prototype.fetchTask = function (taskID) {
    var url = "".concat(API_URL.MAIN).concat(API_URL.TASKS, "/").concat(taskID, "/");
    return this.reqs.fetcher(url);
  };

  // for now just disable drafts for annotations from other users
  HeartexSDK.prototype.fixCompletions = function (task) {
    if (!task.completions) return;
    task.completions.forEach(completion => {
      let id = (completion.createdBy || completion.created_username || "").match(/\d+(?=\)$)/);
      if (id && this.opts.user && +id !== +this.opts.user.pk) {
        completion.noDrafts = true;
      }
    });
  };

  HeartexSDK.prototype.showTask = function (task) {
    var _ref2 = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
        _ref2$addInitial = _ref2.addInitial,
        addInitial = _ref2$addInitial === void 0 ? false : _ref2$addInitial,
        _ref2$addInitialWhenE = _ref2.addInitialWhenEmpty,
        addInitialWhenEmpty = _ref2$addInitialWhenE === void 0 ? false : _ref2$addInitialWhenE,
        _ref2$usePrediction = _ref2.usePrediction,
        usePrediction = _ref2$usePrediction === void 0 ? this.opts.usePrediction : _ref2$usePrediction;

    var studio = this.studio;
    task.data = JSON.stringify(task.data);

    this.fixCompletions(task);

    studio.resetState();
    studio.assignTask(task);
    studio.initializeStore(Serializers.taskToLS(task));
    var cs = studio.completionStore;
    var c;

    // Add drafts
    if (task.drafts) {
      for (const draft of task.drafts) {
        if (draft.completion) {
          // Completion existed - add draft to existed completion
          const draftCompletionPk = String(draft.completion);
          c = cs.completions.find(c => c.pk === draftCompletionPk);
          if (c) {
            c.addVersions({draft: draft.result});
            c.deleteAllRegions({ deleteReadOnly: true });
          } else {
            // that's shouldn't happen
            console.error(`No completion found for pk=${draftCompletionPk}`);
          }
        } else {
          // Completion not found - create new completion from draft
          c = cs.addCompletion({
            draft: draft.result,
            userGenerate: true,
            createdBy: draft.created_username,
            createdAgo: draft.created_ago
          });
        }
        cs.selectCompletion(c.id);
        c.deserializeCompletion(draft.result);
        c.setDraftId(draft.id);
      }
    }

    if (_ref2.loadCompletionIfExists && cs.completions.length > 0 ) {
        cs.selectCompletion(cs.completions[0].id);
    }
    else if (addInitial === true || addInitialWhenEmpty === true && cs.completions.length === 0) {
      c = usePrediction === true && cs.predictions.length > 0 ?
        cs.addCompletionFromPrediction(cs.predictions[0]) :
        cs.addCompletion({userGenerate: true});
      cs.selectCompletion(c.id);
    }

    cs.selected.setupHotKeys();

    studio.setFlags({
      isLoading: false
    });
  };

  HeartexSDK.prototype.getCurrentResults = function () {
    var sel = this.studio.completionStore.selected;
    return sel && sel.serializeCompletion();
  }; // set labeling mode


  HeartexSDK.prototype.setMode = function (mode) {
    this.opts.mode = mode;
  };
  /*
   * Private Methods
   */


  HeartexSDK.prototype._addTask = function (taskID) {
    if (this.opts.task) this.loadedTasks.push(this.opts.task);
    this.opts.task = taskID;
  };

  HeartexSDK.prototype._loadTask = function (url, opts) {
    var self = this;
    var studio = this.studio;
    var req = this.reqs.fetcher(url);
    req.then(function (loadedTask) {
      if (_instanceof(loadedTask, Response) && loadedTask.status === 404) {
        studio.setFlags({
          isLoading: false,
          noTask: true
        });
        return;
      }

      if (_instanceof(loadedTask, Response) && loadedTask.status === 403) {
        studio.setFlags({
          isLoading: false,
          noAccess: true
        });
        return;
      }

      loadedTask.json().then(function (response) {
        /**
         * Convert received data to string for MST support
         */
        self.showTask(response, opts);

        self._addTask(response.id);

        self.cbs.onTaskLoad(response);
      });
    });
  };

  HeartexSDK.prototype._initLabelStudio = function () {
    /*
     * Initialize Label Studio to work with Heartex API
     */
    var self = this;
    var opts = this.opts,
        cbs = this.cbs,
        reqs = this.reqs;
    var options = {
      config: opts.config,
      user: opts.user,
      interfaces: opts.interfaces,
      messages: opts.messages,
      options: opts.options,
      isLoading: true,
      onSubmitCompletion: function onSubmitCompletion(studio, completion) {
        studio.setFlags({
          isLoading: true
        });
        var req = reqs.poster(
          "".concat(API_URL.MAIN).concat(API_URL.TASKS, "/").concat(studio.task.id).concat(API_URL.COMPLETIONS, "/"),
          Serializers.completionToAPI(completion)
        );
        return req.then(function (httpres) {
          return httpres.json().then(function (res) {
            if (res && res.id) completion.updatePersonalKey(res.id.toString());
            cbs.onSubmitCompletion(studio, completion);

            if (opts.mode == "stream") {
              self.labelNextTask({
                usePrediction: opts.usePrediction
              });
            } else {
              studio.setFlags({
                isLoading: false
              });
            }
          });
        });
      },
      onSubmitDraft: function onSubmitDraft(studio, completion) {
            const isNewDraft = !completion.pk;
            const data = Serializers.completionToAPI(completion);
            const urlPrefix = `${API_URL.MAIN}${API_URL.TASKS}/${studio.task.id}`;

            var req;
            if (isNewDraft) {
              // No completion created yet
              if (completion.draftId > 0) {
                const url = `${urlPrefix}${API_URL.DRAFTS}/${completion.draftId}`;
                req = reqs.patch(url, data);
              } else {
                // No draft created yet
                const url = `${urlPrefix}${API_URL.DRAFTS}`;
                req = reqs.poster(url, data);
              }
            } else {
              // Completion already existed
              if (completion.draftId > 0) {
                // Draft already created
                const url = `${urlPrefix}${API_URL.COMPLETIONS}/${completion.pk}${API_URL.DRAFTS}/${completion.draftId}`;
                console.log(url);
                req = reqs.patch(url, data);
              } else {
                // No draft created yet
                const url = `${urlPrefix}${API_URL.COMPLETIONS}/${completion.pk}${API_URL.DRAFTS}`;
                req = reqs.poster(url, data);
              }
            }

            return req.then(httpres => httpres.json()
                .then(function(res) {
                  isNewDraft && res.id && completion.setDraftId(res.id);
                  return res;
                })
                .catch(() => true)
            );
      },
      onUpdateCompletion: function onUpdateCompletion(studio, completion) {
        studio.setFlags({
          isLoading: true
        });
        var req = reqs.patch("".concat(API_URL.MAIN).concat(API_URL.TASKS, "/").concat(studio.task.id).concat(API_URL.COMPLETIONS, "/").concat(completion.pk, "/"), Serializers.completionToAPI(completion));
        return req.then(function (httpres) {
          studio.setFlags({
            isLoading: false
          });
          cbs.onUpdateCompletion(studio, completion);
          if (opts.mode == "stream") {
              self.labelNextTask({
                usePrediction: opts.usePrediction
              });
            } else {
              studio.setFlags({
                isLoading: false
              });
            }
        });
      },
      onDeleteCompletion: function onDeleteCompletion(studio, completion) {
        studio.setFlags({
          isLoading: true
        });
        if (completion.pk && Number.isNaN(parseInt(completion.pk))) {
          studio.setFlags({
            isLoading: false
          });
          return;
        }
        const isOnlyDraft = !completion.pk;
        const urlPrefix = `${API_URL.MAIN}${API_URL.TASKS}/${studio.task.id}`;
        const url = isOnlyDraft
          ? `${urlPrefix}${API_URL.DRAFTS}/${completion.draftId}`
          : `${urlPrefix}${API_URL.COMPLETIONS}/${completion.pk}`;
        var req = reqs.remover(url);
        req.then(function (httpres) {
          studio.setFlags({
            isLoading: false
          });
          cbs.onDeleteCompletion(studio, completion);
        });
      },
      onSkipTask: function onSkipTask(studio) {
        studio.setFlags({
          loading: true
        });
        var req = reqs.poster("".concat(API_URL.MAIN).concat(API_URL.TASKS, "/").concat(studio.task.id).concat(API_URL.CANCEL), JSON.stringify({
          data: JSON.stringify({
            error: "cancelled"
          })
        })); // studio.resetState();

        cbs.onSkipTask(studio);
        if (opts.mode === "stream") self.labelNextTask();

        return req;
      },
      onGroundTruth: function onGroundTruth(studio, completion, value) {
        reqs.patch("".concat(API_URL.MAIN).concat(API_URL.TASKS, "/").concat(studio.task.id).concat(API_URL.COMPLETIONS, "/").concat(completion.pk, "/"), JSON.stringify({
          honeypot: value
        }));
      },
      onLabelStudioLoad: function onLabelStudioLoad(studio) {
        self.studio = studio;

        if (!opts.task) {
          studio.setFlags({
            isLoading: true
          });
          cbs.onLabelStudioLoad(studio); // if (opts.mode === "stream")
          //     self.labelNextTask();
        }
      },
      onEntityCreate: opts.onEntityCreate,
      onEntityDelete: opts.onEntityDelete
    };
    if (window.hasOwnProperty('USE_DRAFTS') && !window.USE_DRAFTS) {
      delete options['onSubmitDraft'];
    }
    return new LabelStudio(opts.elid, options);
  };

  g["HeartexSDK"] = HeartexSDK;
})(window);